
// let & const

// var 声明 以及 变量提升 (Hoisting) 机制

// function getValue(falg) {
//   // var value;
//   if (falg) {
//     var value = 'red';
//     // value = 'red';
//     return value;
//   } else {
//     // console.log(value); // 变量value在此处可访问，值为undefined
//     return null;
//   }
//   // console.log(value); // 变量value在此处可访问，值为undefined
// }

// 无论如何 变量 value 都会被创建。在预编译阶段，变量value 会被提升到函数顶部
// 而初始化操作依旧留在远处执行，意味着else 字句中也能访问到变量，
// 又因为变量尚未初始化 所以值为 undefined

// var value = 'red';
// var value = 'bule';
// var value = 'yellow';



// 块级声明 ES6

// 块级声明用于声明在指定块的作用域之外无法访问的变量
// 存在于： 函数内部、块中(字符 { 和 } 之间的区域)

// function getValue2(flag) {
//   if (flag) {
//     let value = 'red';

//     return value; // 执行流离开if 块，let声明的变量会立刻被销毁
//   } else {
//     // console.log(value); // 变量value在此处不存在
//     return null;
//   }
//   // console.log(value); // 变量value在此处不存在
// }

// var value = 1;
// let value = 2;
// const value = 3;

// let value = 1;
// let value = 2;
// const value = 2;
// const value = 3;

// 都会抛出语法错误

// var num = 1;
// var flag = true;
// if (flag) {
//   let num = 2;
//   const num = 2;
// }

// 如果当前作用域内嵌一个作用域，便可在内嵌的作用域里使用let、const 声明同名变量
// 内部块中的声明会遮蔽全局作用域中的同名变量，但后者只有内部快中才能访问;

// const name = 'Tom';

// const name; // 会报语法错误，必须初始化

// const obj = {
//   a: 1
// };
// obj.a = 111;

// obj = {
//   a: 2
// };

// Uncaught TypeError: Assignment to constant variable.

// let flag = true;
// if (flag) {
//   console.log(typeof value); // 会抛出错误
//   // let value = 1;
//   const value = 1; // 此语句不执行 此时的 value 还处于TDZ中
// }

// console.log(typeof value); // 不会报错，为 undefiend
// let flag = true;
// if (flag) {
//   // let value = 1;
//   const value = 1; // 此语句不执行 此时的 value 还处于TDZ中
// }


/**
 * 临时死区: TDZ (Temporal Dead Zone)
 * 
 * 虽然ECMAScript 标准并没有明确提到，但人们常用它来描述let 和 const 的不提升效果
 * 
 * JS引擎 在扫描代码发现变量声明的时，有两种情况。一，要么将它们提升至作用域顶部(var声明)，二，要么将声明放到TDZ中(let、const)。
 * 访问TDZ中的变量会触发运行时错误。只有 执行过 变量声明语句后，变量才会从TDZ中移出，才能正常访问。
 * 
 */


// for (var i = 0; i < 10; i++) {}
// console.log(i); // 10
// for (let i = 0; i < 10; i++) {}
// console.log(i); // 抛出错误 i 只存在于for 循环中

var funcs = [];

// for (var i = 0; i < 10; i++) {
//   // funcs.push(function() { // 方式1 var for循环
//   //   console.log(i);
//   // });
//   funcs.push((function(i) { // 方式2 IIFE 创建副本存储
//     return function() {
//       console.log(i);
//     }
//   })(i));
// }

// for (let i = 0; i < 10; i++) {
//   funcs.push(function() { // 方式3 let for循环
//     console.log(i);
//   });
// }
// let 声明在循环内部的行为是标准中专门定义的，不一定与let 的不提升特性相关

// for (const i = 0; i < 10; i++) { // Assignment to constant variable.
//   funcs.push(function() { // 方式4 const for 循环
//     console.log(i);
//   });
// }
// 因为是每次迭代循环时，都会创建一个新变量，const声明的常量不可更改、如果不是常量就可以使用const声明的循环

// funcs.forEach(function (func) {
//   // func(); // 10 次 10 // 方式1
//   func(); // 0 ~ 9 // 方式2 \ 方式3
// })

// let obj = {
//   a: 1,
//   b: 2,
//   c: 3
// }
// let arr = [1, 2, 3, 4]

// for (let k in obj) {
//   console.log(obj[k])
// }
// for (const k in obj) {
//   console.log(obj[k])
// }
// for (const k of arr) {
//   console.log(k);
// }

// for...in 和 for ... of 中
// 每次迭代不会像for循环一样 修改已有的绑定，而是创建一个新的绑定


var RegExp = 'Hello';
console.log(window.RegExp); // 'Hello'
// 即使全局对象RegExp 定义在window上， 也不能幸免 被var 声明覆盖

var ncz = 'Hi';
console.log(window.ncz); // 'Hi'

var RegExp = 'Hello';
console.log(window.RegExp === RegExp); // false
// 即使全局对象RegExp 定义在window上， 也不能幸免 被var 声明覆盖

var ncz = 'Hi';
console.log(window.ncz === ncz); // false


/**
 * var:
 * 1. 具有变量提升 (Hoisting) 机制
 * 2. 可重复声明
 * 3. 全局绑定(全局污染)
 * 
 * 
 * let & const: (共同)
 * 
 * 1. 块级声明
 * 2. 没有变量提升(Hoisting)机制
 * 3. 执行流离开块级区域，块声明的变量会立刻被销毁
 * 4. 不可重复声明已存在的标识符(变量)
 * 5. 具有临时死区(TDZ: temporal dead zone)
 * 6. 普通循环时，每次循环都会创建一个新的变量绑定 并初始化当前值
 * 7. 全局块作用域绑定不会覆盖
 * 
 * 不同:
 * 
 * let:
 * 1. 定义的变量的值可以任意修改
 * 2. 标准中专门定义的循环内部行为
 * 
 * const:
 * 1. const声明必须要初始化(赋值)
 * 2. 无论严格还是非严格模式，如果定义了常量，不能修改
 * 3. const 声明的对象可以修改值但不能修改绑定(内存地址)
 * 4. 普通for循环不可使用(常量时)
 */
